﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace TOP
{
    public partial class OptionsCollection
    {
        #region Load

        public static OptionsCollection LoadOptions(string filename)
        {
            StreamReader sr = new StreamReader(filename);
            string readLine = sr.ReadToEnd(),
                line = "",
                name = "",
                array = "",
                varname = "";
            OptionsCollection currentOC = new OptionsCollection("error");
            OptionFunction function = OptionFunction.name,
                lastFunction = OptionFunction.end;
            List<string> terms = new List<string>();
            List<OptionFunction> funcs = new List<OptionFunction>();
            bool inQuote = false,
                inVarName = false;
            for (int i = 0; i < readLine.Length; i++)
            {
                switch (readLine[i])
                {
                    case '"':
                        inQuote = !inQuote;
                        break;
                    case '\\':
                        if (inQuote) goto default;
                        i++;
                        goto default;
                    case '[':
                        if (inQuote) goto default;
                        i++;
                        inVarName = true;
                        goto default;
                    case ']':
                        if (inQuote) goto default;
                        inVarName = false;
                        try
                        {
                            line += environmentVars[varname];
                        }
                        catch
                        {
                            line += "[" + varname + "]";
                            Console.Write("(!) Environment Variable not found: " + varname);
                        }
                        varname = "";
                        break;
                    case ' ':
                        if (inQuote) goto default;
                        break;
                    case ':':
                        if (inQuote) goto default;
                        if (lastFunction == OptionFunction.name)
                            currentOC = currentOC.Add(name, line);
                        if (lastFunction == OptionFunction.array)
                        {
                            if (line == "") break;
                            terms.Add(array);
                            funcs.Add(function);
                            currentOC = currentOC.Add(name, array);
                            //currentOC = currentOC.Parent;
                            array = "";
                        }
                        terms.Add(line);
                        funcs.Add(OptionFunction.name);
                        name = line;
                        if (name == "BuccOptions")
                            currentOC = new OptionsCollection("BuccOptions");
                        lastFunction = OptionFunction.name;
                        function = OptionFunction.number;
                        line = "";
                        break;
                    case ',':
                        if (inQuote) goto default;
                        function = OptionFunction.array;
                        lastFunction = OptionFunction.array;
                        array = array == "" ? line : array + "," + line;
                        terms.Add(line);
                        funcs.Add(function);
                        line = "";
                        break;
                    case ';':
                        if (inQuote) goto default;
                        if (lastFunction == OptionFunction.end)
                            currentOC = currentOC.Parent;
                        if (line == "") break;
                        terms.Add(line);
                        funcs.Add(function);
                        switch (function)
                        {
                            case OptionFunction.action:
                                currentOC = currentOC.Add(name, line);
                                break;
                            case OptionFunction.array:
                                currentOC = currentOC.Add(name, array + "," + line);
                                break;
                            case OptionFunction.name:
                                currentOC = currentOC.Add(name, line);
                                break;
                            case OptionFunction.number:
                                currentOC = currentOC.Add(name, line);
                                break;
                            case OptionFunction.text:
                                currentOC = currentOC.Add(name, line);
                                break;
                        }
                        function = OptionFunction.name;
                        lastFunction = OptionFunction.end;
                        currentOC = currentOC.Parent;
                        line = "";
                        array = "";
                        break;
                    case '\n':
                    case '\r':
                        if (inQuote) goto default;
                        break;
                    default:
                        if (!"0123456789".Contains(readLine[i]) && function == OptionFunction.number)
                            function = OptionFunction.text;
                        if (inVarName)
                            varname = varname.Insert(varname.Length, readLine[i].ToString());
                        else
                            line = line.Insert(line.Length, readLine[i].ToString());
                        break;
                }
            }

            if (currentOC.Parent != null)
            {
                Console.Write("(!) Error in optionsfile: not enough semicolons encountered.");
                while (currentOC.Parent != null)
                    currentOC = currentOC.Parent;
            }

            return currentOC;
        }

        #endregion

        #region Save

        public static void SaveOptions(OptionsCollection optionCol, string filename)
        {
            StreamWriter sw = new StreamWriter(filename);
            string code = optionCol.ToCode();
            sw.Write(code);
            sw.Close();
            sw.Dispose();
        }

        public string ToCode()
        {
            int indent = 0;
            string source = "";

            foreach (string name in SubTree)
            {
                for (int i = 0; i < indent; i++)
                    source += " ";
                bool quotes = values[name].Value.IndexOfAny(new char[] { ' ', '\\', ':', ';', ',', '[', ']', '\n', '\r' }) != -1 && values[name].Value != "";
                source += name + ":" + (quotes ? "\"" : "") + values[name].Value + (quotes ? "\"" : "") + "\n";
                if (this.values.ContainsKey(name))
                    source += this.values[name].ToCode(indent) + ";\n";
            }

            return source;
        }

        public string ToCode(int indent)
        {
            string source = "";
            
            foreach (string name in SubTree)
            {
                for (int i = 0; i < indent; i++)
                    source += " ";
                source += name + ":\n";
                if (this.values.ContainsKey(name))
                    source += this.values[name].ToCode(indent) + ";\n";
            }

            return source;
        }

        #endregion
    }
}
